//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  Low level interfaces that allow separate components to use objects that are defined
//               in completely independant libraries so long as they satisfy the basic interface requirements.
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either express or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow.Interfaces
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in September, 2007.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Drawing;

using MapWindow.Main;
using MapWindow.Main.EventArguments;
using MapWindow.Geometries;
using MapWindow.Data;
using MapWindow.Components.Legend;

using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;

namespace MapWindow.Drawing
{
    /// <summary>
    /// The only difference between an EventList&lt;IMapLayer&gt; and this collection is that
    /// this provides methods that allow access by an object Key, and will pass 
    /// </summary>
    public class MapLayerCollection: EventList<IMapLayer>, IMapLayerCollection
    {
       
        private Dictionary<object, IMapLayer> _Dictionary;
      
        private bool _IsBusy;
     
        private bool _PointsValid; 
       
        private bool _LineStringsValid;    
     
        private bool _PolygonsValid;

        private int _NumMapPointLayers;
       
        private int _NumMapLineStringLayers;

        private int _NumMapPolygonLayers;

        private List<IMapLineStringLayer> _MapLineStringLayers;

        private List<IMapPointLayer> _MapPointLayers;
      
        private List<IMapPolygonLayer> _MapPolygonLayers;
        
        private string _LegendText;
       
        private bool _CancelDrawing;

        private Matrix _WorldTransform;

        private bool _Visible;


        /// <summary>
        /// This propogates the cancel message down through the drawing levels
        /// to ensure that the current drawing action is terminated as soon
        /// as possible in order to exit gracefully.
        /// </summary>
        public virtual bool CancelDrawing
        {
            get { return _CancelDrawing; }
            set
            {
                _CancelDrawing = value;
                // Loop through all the layers
                for (int lyr = 0; lyr < this._List.Count; lyr++)
                {
                    _List[lyr].CancelDrawing = value;
                }

            }
        }

       

        /// <summary>
        /// Creates a new blank instance of a MapLayer collection.  This is especially useful
        /// for tracking layers that can draw themselves.  This does not concern itself with
        /// view extents like a dataframe, but rather is a grouping of layers that is itself
        /// also an IMapLayer.
        /// </summary>
        public MapLayerCollection()
        {
            _Dictionary = new Dictionary<object, IMapLayer>();
            _Visible = true;
            
            ListChanged += new EventHandler(MapLayerCollection_ListChanged);
        }

       

        #region Methods

        /// <summary>
        /// This overload automatically constructs a new MapLayer from the specified
        /// feature layer with the default drawing characteristics and returns a valid
        /// IMapLayer which can be further cast into a MapPointLayer, MapLineLayer or
        /// a MapPolygonLayer, depending on the data that is passed in.
        /// </summary>
        /// <param name="featureLayer">Any valid IFeatureLayer that does not yet have drawing characteristics</param>
        /// <returns>A newly created valid implementation of IMapLayer which at least gives a few more common
        /// drawing related methods and can also be cast into the appropriate Point, Line or Polygon layer.</returns>
        public virtual IMapLayer Add(IFeatureLayer featureLayer)
        {
            return Add(featureLayer, null);
        }

        /// <summary>
        /// This overload automatically constructs a new MapLayer from the specified
        /// feature layer with the default drawing characteristics and returns a valid
        /// IMapLayer which can be further cast into a MapPointLayer, MapLineLayer or
        /// a MapPolygonLayer, depending on the data that is passed in.
        /// </summary>
        /// <param name="featureLayer">Any valid IFeatureLayer that does not yet have drawing characteristics</param>
        /// <param name="progHandler">Any valid implementation of the IProgHandler interface for progress messages</param>
        /// <returns>A newly created valid implementation of IMapLayer which at least gives a few more common
        /// drawing related methods and can also be cast into the appropriate Point, Line or Polygon layer.</returns>
        public virtual IMapLayer Add(IFeatureLayer featureLayer, IProgHandler progHandler)
        {
            if (featureLayer is IMapPointLayer)
            {
                // This is already a valid MapLayer
                if (OnBeforeItemAdded((IMapPointLayer)featureLayer) == true) return null; // Canceled
                _List.Add((IMapPointLayer)featureLayer);
                OnAfterItemAdded((IMapPointLayer)featureLayer, _List.Count - 1);
                OnListChanged();
                return (IMapLayer)featureLayer;
            }
            if (featureLayer is IMapLineStringLayer)
            {
                // This is already a valid MapLineStringLayer
                if (OnBeforeItemAdded((IMapLineStringLayer)featureLayer) == true) return null; // Canceled
                _List.Add((IMapLineStringLayer)featureLayer);
                OnAfterItemAdded((IMapLineStringLayer)featureLayer, _List.Count - 1);
                OnListChanged();
                return (IMapLayer)featureLayer;
            }
            if (featureLayer is IMapPolygonLayer)
            {
                // This is already a valid MapLineStringLayer
                if (OnBeforeItemAdded((IMapPolygonLayer)featureLayer) == true) return null; // Canceled
                _List.Add((IMapPolygonLayer)featureLayer);
                OnAfterItemAdded((IMapPolygonLayer)featureLayer, _List.Count - 1);
                OnListChanged();
                return (IMapLayer)featureLayer;
            }
            if (featureLayer is IMapFeatureLayer)
            {
                // This also is already a valid MapLayer, though it's type
                // is clarified internally.  We don't need to instantiate
                // a new object here.
                if (OnBeforeItemAdded((IMapFeatureLayer)featureLayer) == true) return null; // Canceled
                _List.Add((IMapFeatureLayer)featureLayer);
                OnAfterItemAdded((IMapFeatureLayer)featureLayer, _List.Count - 1);
                OnListChanged();
                return (IMapLayer)featureLayer;
            }
            if (featureLayer.FeatureType == FeatureTypes.Point)
            {
                System.Diagnostics.Debug.WriteLine("Adding a Line Feature Layer");
                MapPointLayer pl = new MapPointLayer(featureLayer, progHandler);
                if (OnBeforeItemAdded(pl) == true) return null; // Canceled
                _List.Add(pl);
                OnAfterItemAdded(pl, _List.Count - 1);
                OnListChanged();
                return pl;
            }
            else if (featureLayer.FeatureType == FeatureTypes.Line)
            {
                System.Diagnostics.Debug.WriteLine("Adding a Line Feature Layer");
                MapLineStringLayer ll = new MapLineStringLayer(featureLayer);
                if (OnBeforeItemAdded(ll) == true) return null; // Canceled
                _List.Add(ll);
                OnAfterItemAdded(ll, _List.Count - 1);
                OnListChanged();
                return ll;
            }
            else if (featureLayer.FeatureType == FeatureTypes.Polygon)
            {
                System.Diagnostics.Debug.WriteLine("Adding a Polygon Feature Layer");
                MapPolygonLayer pl = new MapPolygonLayer(featureLayer);
                if (OnBeforeItemAdded(pl) == true) return null; // Canceled
                _List.Add(pl);
                OnAfterItemAdded(pl, _List.Count - 1);
                OnListChanged();
                return pl;
            }

            return null;
            //throw new NotImplementedException("Right now only point types are supported.");
        }


        /// <summary>
        /// Disposes all the members of this collection, even though this collection itself has no unmannaged members
        /// </summary>
        public virtual void Dispose()
        {
            foreach (IMapLayer layer in _List)
            {
                try
                {
                    // Try to dispose all the layers.  This can fail if the object has already been disposed.
                    layer.Dispose();
                }
                catch(Exception ex)
                {
                    // Write a message about a failure, but otherwise continue to the next layer.
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }
        }

        /// <summary>
        /// Draws all the individual layers of this collection to the specified device.
        /// </summary>
        /// <param name="device"></param>
        public virtual void Draw(Device device)
        {
            if (_Visible == false)
            {
                OnAfterDrawing(device, true, null);
                return;
            }
            try
            {
                if (OnBeforeDrawing(device) == true)
                {
                    // The draw event was cancelled by an outside event handler
                    OnAfterDrawing(device, true, null);
                    return;
                }
                foreach (IMapLayer layer in _List)
                {
                    try
                    {
                        // Attempt to draw each object in turn.
                        layer.Draw(device);
                    }
                    catch(Exception ex)
                    {
                        // It doesn't matter if these fail individually.  Try to draw the others.
                        System.Diagnostics.Debug.WriteLine(ex.Message);
                    }
                }

            }
            catch(Exception ex)
            {
                OnAfterDrawing(device, false, ex);
            }
        }


        /// <summary>
        /// Draws the renderable item onto the specified graphics object.  No changes
        /// will be made to the graphics object for scaling or positioning and it is
        /// assumed that any clip-handling has been handled beforehand.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        public virtual void Draw(System.Drawing.Graphics g)
        {
            Draw(g, Rectangle.Empty, Rectangle.Empty);
        }

        /// <summary>
        /// Draws the renderable item onto the specified graphics object.  If the
        /// clipRectangle doesn't intersect the target, no drawing will take place.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        /// <param name="clipRectangle">The bounds within which to draw</param>
        public virtual void Draw(System.Drawing.Graphics g, Rectangle clipRectangle)
        {   
            Draw(g, clipRectangle, Rectangle.Empty);
        }


        

        /// <summary>
        /// Draws the renderable item onto the specified graphics object.  If the
        /// clipRectangle doesn't intersect the target, no drawing will take place.
        /// The renderable item will be stretched and scaled to fit in the specified target.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        /// <param name="clipRectangle">The bounds within which to draw</param>
        /// <param name="target">This specifies the pixel "Fit To" rectangle</param>
        public virtual void Draw(System.Drawing.Graphics g, Rectangle clipRectangle, Rectangle target)
        {
            _IsBusy = true;
            System.Drawing.Drawing2D.Matrix oldMatrix = null;
            
            try
            {
                if (g == null)
                {
                    OnPaintCancelled(new ArgumentNullException("g cannot be null."), g, Rectangle.Empty, Rectangle.Empty);
                    return;
                }

                oldMatrix = (System.Drawing.Drawing2D.Matrix)g.Transform.Clone();
                System.Drawing.Drawing2D.Matrix mat = g.Transform;

                if (this.Visible == false)
                {
                    OnAfterPainting(g, clipRectangle, target);
                    return;
                }

                // The OnBeforePainting method can cancel the drawing
                if (OnBeforePainting(g, clipRectangle, target) == true)
                {
                     OnPaintCancelled(new OperationCanceledException("Canceled by OnBeforePainting"), g, clipRectangle, target);
                     return;
                }

               
                // Check to see if we have any members in the list to draw
                if (_List == null)
                {
                    OnAfterPainting(g, clipRectangle, target);
                    return;
                }
                if (_List.Count <= 0)
                {
                    OnAfterPainting(g, clipRectangle, target);
                    return; // No layers to draw
                }

                // Apply the Clip Rectangle before any math is taken into consideration
                if (clipRectangle.IsEmpty == false)
                {
                    g.IntersectClip(clipRectangle);
                }


                // This is not as smooth as a map frame, but acts as a backup for people that just want to get right to it
                if (target != System.Drawing.Rectangle.Empty)
                {

                    // 'mat' exists because trying to Scale or Translate the Transform property directly doesn't work
                    mat = new System.Drawing.Drawing2D.Matrix();
                    
                    // First flip the Y direction so that values increase as you move up (not the default orientation)
                    mat.Multiply(new System.Drawing.Drawing2D.Matrix(1f, 0f, 0f, -1f, 0f, 0f));

                    // Calculate the scale difference between pixel coordinates and map coordinates
                    IEnvelope env = this.Envelope;
                    float scaleX = Convert.ToSingle(target.Width / env.Width);
                    float scaleY = Convert.ToSingle(target.Height / env.Height);
                    mat.Scale(scaleX, scaleY);

                    mat.Translate(Convert.ToSingle(-env.MinX), Convert.ToSingle(-env.MaxY));

                    g.Transform = mat;

                }

                // Loop through all the layers
                for (int lyr = 0; lyr < this._List.Count; lyr++)
                {

                    // Check to see if this was canceled
                    if (_CancelDrawing == true)
                    {
                        OnPaintCancelled(new OperationCanceledException("CancelDrawing was true"), g, clipRectangle, target);
                        if (oldMatrix != null) g.Transform = oldMatrix;
                    }

                    _List[lyr].Draw(g, clipRectangle); // We don't need to specify the target here because we have already handled that
                }
            }
            catch (Exception ex)
            {
               
                OnPaintCancelled(ex, g, clipRectangle, target);
                
               
            }
            finally
            {
                if (oldMatrix != null) g.Transform = oldMatrix;
            }
            OnAfterPainting(g, clipRectangle, target);
            
        }

        /// <summary>
        /// Not Implemented
        /// </summary>
        /// <param name="g"></param>
        /// <param name="DrawingArea"></param>
        /// <returns></returns>
        public int DrawLegendItem(System.Drawing.Graphics g, System.Drawing.Rectangle DrawingArea)
        {
            throw new NotImplementedException("The method or operation is not implemented.");
        }

        #endregion

        #region Properties

        /// <summary>
        /// Directly work with the dictionary.  This is a secondary device and only gives you
        /// a quick method for accessing specific members.  Members are not added to this 
        /// automatically, and are not removed from it when you remove them from the list.
        /// However, this is especially useful for tracking specific layers so that you
        /// don't have to worry about how the layers are re-ordered by the user.
        /// </summary>
        public virtual Dictionary<object, IMapLayer> Dictionary
        {
            get
            {
                return _Dictionary;
            }
        }

        /// <summary>
        /// The envelope that contains all of the layers for this data frame.  Essentially this would be
        /// the extents to use if you want to zoom to the world view.
        /// </summary>
        public virtual IEnvelope Envelope
        {
            get
            {
                IEnvelope env;
                if (_List == null) return new Envelope();
                if (_List.Count == 0) return new Envelope();
                env = _List[0].Envelope;
                for (int lyr = 0; lyr < _List.Count; lyr++)
                {
                    env.ExpandToInclude(_List[lyr].Envelope);
                }
                return env;
            }
        }


        /// <summary>
        /// Returns all the layers with point features in a list of IMapPointLayers
        /// returns the values already cast as MapPointLayers.
        /// </summary>
        public virtual List<IMapPointLayer> GetPointLayers()
        {
            if (_PointsValid == false)
            {
                RecountPoints();
            }
            return _MapPointLayers;
        }

        /// <summary>
        /// Returns the N'th PointLayer where N is the index where only point layers are counted
        /// </summary>
        /// <param name="N"></param>
        /// <returns></returns>
        public virtual IMapPointLayer GetPointLayer(int N)
        {
            if (_PointsValid == false) RecountPoints();
            return _MapPointLayers[N];
        }

      
        /// <summary>
        /// Returns a list of IMapLineStringLayer interfaces representing each of the current MapLayers that happens
        /// to specifically be a LineStringLayer.  The index values will probably not match the index value in 
        /// the complete list of layers.
        /// </summary>
        public virtual List<IMapLineStringLayer> GetLineStringLayers()
        {
            if (_LineStringsValid == false) RecountLineStrings();
            return _MapLineStringLayers;
        }

        /// <summary>
        /// Returns the N'th LineStringLayer where N is the index if only LineString layers are counted
        /// </summary>
        /// <param name="N">The integer index specifying the LineString Layer</param>
        /// <returns></returns>
        public virtual IMapLineStringLayer GetLineStringLayer(int N)
        {
            if (_LineStringsValid == false) RecountLineStrings();
            return _MapLineStringLayers[N];
        }

        /// <summary>
        /// Returns a list of IMapPolygonLayer interfaces representing each of the current MapLayers that happens
        /// to specifically be a LineStringLayer.  The index values will probably not match the index value in 
        /// the complete list of layers.
        /// </summary>
        public virtual List<IMapPolygonLayer> GetPolygonLayers()
        {
            if (_PolygonsValid == false) RecountPolygons();
            return _MapPolygonLayers;
        }

        /// <summary>
        /// Returns the N'th IMapPolygonLayer where N is the index if only LineString layers are counted
        /// </summary>
        /// <param name="N">The integer index specifying the LineString Layer</param>
        /// <returns></returns>
        public virtual IMapPolygonLayer GetPolygonLayer(int N)
        {
            if (_PolygonsValid == false) RecountPolygons();
            return _MapPolygonLayers[N];
        }


        /// <summary>
        /// Boolean, true if the drawing code is executing
        /// </summary>
        public virtual bool IsBusy
        {
            get { return _IsBusy; }
            
        }


        /// <summary>
        /// Gets or sets the complete list of legend items contained within this legend item
        /// </summary>
        public virtual ArrayList LegendItems
        {
            get {
                ArrayList temp = new ArrayList();
                foreach (IMapLayer ml in _List)
                {
                    temp.Add(ml);
                }
                return temp;
            }
            
        }

        /// <summary>
        /// The text that will appear in the legend 
        /// </summary>
        public virtual string LegendText
        {
            get { return _LegendText; }
            set { _LegendText = value; }
        }
       

        /// <summary>
        /// Gets the total count of Point layers in this collection
        /// </summary>
        public virtual int NumPointLayers
        {
            get
            {
                if (_PointsValid == false) RecountPoints();
                return _MapPointLayers.Count;
            }
        }

        /// <summary>
        /// Gets the total count of LineString layers in this collection
        /// </summary>
        public virtual int NumLineStringLayers
        {
            get
            {
                if (_LineStringsValid == false) RecountLineStrings();
                return _MapLineStringLayers.Count;
            }
        }

        /// <summary>
        /// Gets the total count of Polygon layers in this collection
        /// </summary>
        public virtual int NumPolygonLayers
        {
            get
            {
                if (_PolygonsValid == false) RecountPolygons();
                return _MapPolygonLayers.Count;
            }
        }

        /// <summary>
        /// Essentially filters the list of all layers and only returns those that have point features, and
        /// returns interfaces that are already cast as IMapPointLayers.
        /// </summary>
        public virtual List<IMapPointLayer> PointLayers
        {
            get
            {
                List<IMapPointLayer> result = new List<IMapPointLayer>();
                foreach (IMapLayer ml in this._List)
                {
                    if (ml is IMapPointLayer)
                    {
                        result.Add((IMapPointLayer)ml);
                    }
                }
                return result;
            }
        }

        /// <summary>
        /// Gets a list of IMapLineStringLayer interfaces representing each of the current MapLayers that happens
        /// to specifically be a LineStringLayer.  The index values will probably not match the index value in 
        /// the complete list of layers.
        /// </summary>
        public virtual List<IMapLineStringLayer> LineStringLayers
        {
            get
            {
                List<IMapLineStringLayer> result = new List<IMapLineStringLayer>();
                foreach (IMapLayer ml in this._List)
                {
                    if (ml is IMapLineStringLayer)
                    {
                        result.Add((IMapLineStringLayer)ml);
                    }
                }
                return result;
            }
        }

        /// <summary>
        /// Gets or sets the transform that affects all the members of this layer collection.
        /// </summary>
        public virtual Matrix WorldTransform
        {
            get { return _WorldTransform; }
            set { _WorldTransform = value; }
        }
       

        /// <summary>
        /// If this is false, then the drawing function will not render anything.
        /// Warning!  This will also prevent any execution of calculations that take place
        /// as part of the drawing methods and will also abort the drawing methods of any
        /// sub-members to this IRenderable.
        /// </summary>
        public bool Visible
        {
            get { return _Visible; }
            set { _Visible = value; }
        }


        #endregion

        #region Events

        #region Paint

        /// <summary>
        /// Fires before the paint event.  If the cancel property of this event is set to true,
        /// the paint event will be canceled.  The rect property is the "fit-to" property for
        /// this event.
        /// </summary>
        public virtual event EventHandler<PaintCancel> BeforePainting;

        /// <summary>
        /// Fires the BeforeDrawing event
        /// </summary>
        /// <param name="g"></param>
        /// <param name="clipRect"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        protected virtual bool OnBeforePainting(System.Drawing.Graphics g, System.Drawing.Rectangle clipRect, System.Drawing.Rectangle target)
        {
            if (BeforeDrawing != null)
            {
                PaintCancel e = new PaintCancel(g, clipRect, target);
                BeforePainting(this, e);
                if (e.Cancel == true) return true;
            }
            return false;
        }

        /// <summary>
        /// Fires after the paint event is complete.  The "clipRectangle" was actually the 
        /// "Fit-To" rectangle that the frame is drawn within.
        /// </summary>
        public virtual event EventHandler<PaintCompleted> AfterPainting;

        /// <summary>
        /// The drawing method finished, and exited normally.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface used by the draw method.</param>
        /// <param name="clipRect">The System.Drawing.Rectangle defining the area to actually perform drawing</param>
        /// <param name="target">The destination rectangle for the System.Drawing.Rectangle where the result should be drawn</param>
        protected virtual void OnAfterPainting(System.Drawing.Graphics g, System.Drawing.Rectangle clipRect, System.Drawing.Rectangle target)
        {
            if (AfterPainting != null)
            {
                AfterPainting(this, new PaintCompleted(g, clipRect, target));
            }
        }

        /// <summary>
        /// Internal Method to fire the Cancel Event.  This caries the same basic information that the 
        /// DrawingComplete method, plus an exception if any as well as a boolean indicating the draw method was cancelled.
        /// </summary>
        /// <param name="Error">A System.Exception showing the error that caused the cancelation</param>
        /// <param name="g">A System.Drawing.Graphics surface that drawing was taking place on</param>
        /// <param name="clipRect">The ClipRectangle or Rectangle.Empty</param>
        /// <param name="target">The Destination Rectangle or Rectangle.Empty</param>
        protected virtual void OnPaintCancelled(System.Exception Error, System.Drawing.Graphics g, System.Drawing.Rectangle clipRect, System.Drawing.Rectangle target)
        {
            if (AfterPainting != null)
            {
                AfterPainting(this, new PaintCompleted(Error, true, g, clipRect, target));
            }
        }


        #endregion


        #region Draw Events

        /// <summary>
        /// Occurs when the Draw method is called and provides the option to cancel the event before anything is actually drawn.
        /// </summary>
        public virtual event EventHandler<DrawCancel> BeforeDrawing;

        /// <summary>
        /// Fires the event that occurs before drawing to a directX map
        /// </summary>
        /// <param name="device">A DirectX.Direct3D.Device that will be drawn to</param>
        /// <returns>Boolean, that if true should cancel the draw event.</returns>
        protected virtual bool OnBeforeDrawing(Device device)
        {
            if (BeforeDrawing == null) return false;
            DrawCancel e = new DrawCancel(device);
            BeforeDrawing(this, e);
            return e.Cancel;
        }


        /// <summary>
        /// Occurs after the contents of this MapLayerCollection have been rendered to the Microsoft.DirectX.Direct3D.Device
        /// </summary>
        public virtual event EventHandler<DrawCompleted> AfterDrawing;

        /// <summary>
        /// Fires an event when we are done with the drawing code, whether the drawing was successful, cancelled, or threw an exception.
        /// </summary>
        /// <param name="device">A DirectX.Direct3D.Device that rendering took place on</param>
        /// <param name="cancelled">If true, the event was cancelled normally</param>
        /// <param name="ex">An Exception in the event that this was an error.  This will normally be null.</param>
        protected virtual void OnAfterDrawing(Device device, bool cancelled, Exception ex)
        {
            if (AfterDrawing == null) return;
            DrawCompleted e;
            if (ex == null)
            {
                e = new DrawCompleted(device, cancelled);
            }
            else
            {
                e = new DrawCompleted(device, ex);
            }
            AfterDrawing(this, e);
        }


        #endregion

        #endregion

        /// <summary>
        /// When changes are made to the master list, these lists are invalidated so that if they are
        /// queried again, they will have to be accessed by checking the master list instead of the cache
        /// </summary>
        private void RecountLineStrings()
        {
            _MapLineStringLayers = new List<IMapLineStringLayer>();
            foreach (IMapLayer ml in this._List)
            {
                if (ml is IMapLineStringLayer)
                {
                    _MapLineStringLayers.Add((IMapLineStringLayer)ml);
                }
            }
            _NumMapLineStringLayers = _MapLineStringLayers.Count;
            _LineStringsValid = true;
        }

        /// <summary>
        /// When changes are made to the master list, these lists are invalidated so that if they are
        /// queried again, they will have to be accessed by checking the master list instead of the cache
        /// </summary>
        private void RecountPoints()
        {
            
            _MapPointLayers = new List<IMapPointLayer>();
            foreach (IMapLayer ml in this._List)
            {
                if (ml is IMapPointLayer)
                {
                    _MapPointLayers.Add((IMapPointLayer)ml);
                }
            }
            _NumMapPointLayers = _MapPointLayers.Count;
            _PointsValid = true;
        }

        /// <summary>
        /// Recounts the Poylgon layers to re-validate the cached list
        /// </summary>
        private void RecountPolygons()
        {
            _MapPolygonLayers = new List<IMapPolygonLayer>();
            foreach (IMapLayer ml in this._List)
            {
                if (ml is IMapPolygonLayer)
                {
                    _MapPolygonLayers.Add((IMapPolygonLayer)ml);
                }
            }
            _NumMapPolygonLayers = _MapPolygonLayers.Count;
            _PolygonsValid = true;
        }
        
        /// <summary>
        /// Handles the situation where the List is changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void MapLayerCollection_ListChanged(object sender, EventArgs e)
        {
            _PointsValid = false;
            _LineStringsValid = false;
        }
       
    }
}
